const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');
const jsdom = require('jsdom');
const iconv = require('iconv-lite');
const puppeteer = require('puppeteer');
const AdmZip = require('adm-zip');
const { createExtractorFromFile } = require('node-unrar-js');
const JSZip = require('jszip');
const xml2js = require('xml2js');
const basePath = 'https://www.1ppt.com/';
// 下载的最大页码
let page = 38;
// 最后一页的页面
let minPage = 1;
// 分类链接
const baseUrl = `${basePath}/xiazai/zongjie/`;
// 添加删除最后一页的函数
const removeLastSlide = async (pptxPath) => {
    try {
        console.log('# 正在删除最后一页...');
        
        // 读取 PPTX 文件
        const data = fs.readFileSync(pptxPath);
        const zip = await JSZip.loadAsync(data);
        
        // 读取 presentation.xml 获取幻灯片列表
        const presentationXml = await zip.file('ppt/presentation.xml').async('string');
        const parser = new xml2js.Parser();
        const builder = new xml2js.Builder();
        const presentation = await parser.parseStringPromise(presentationXml);
        
        // 获取幻灯片 ID 列表
        const slideIdList = presentation['p:presentation']['p:sldIdLst'][0]['p:sldId'];
        
        if (!slideIdList || slideIdList.length === 0) {
            console.log('# 警告: PPT 中没有幻灯片');
            return;
        }
        
        if (slideIdList.length === 1) {
            console.log('# 警告: PPT 只有一页，不删除');
            return;
        }
        
        // 获取最后一张幻灯片的 rId
        const lastSlide = slideIdList[slideIdList.length - 1];
        const lastSlideRId = lastSlide.$['r:id'];
        
        // 读取 presentation.xml.rels 获取幻灯片文件名
        const relsXml = await zip.file('ppt/_rels/presentation.xml.rels').async('string');
        const rels = await parser.parseStringPromise(relsXml);
        const relationships = rels['Relationships']['Relationship'];
        
        // 找到最后一页对应的文件
        const lastSlideRel = relationships.find(rel => rel.$.Id === lastSlideRId);
        const lastSlideFile = 'ppt/' + lastSlideRel.$.Target;
        const lastSlideNumber = lastSlideFile.match(/slide(\d+)\.xml/)[1];
        
        console.log(`# 找到最后一页: ${lastSlideFile}`);
        
        // 删除最后一张幻灯片引用
        slideIdList.pop();
        
        // 删除 relationship
        const filteredRels = relationships.filter(rel => rel.$.Id !== lastSlideRId);
        rels['Relationships']['Relationship'] = filteredRels;
        
        // 更新 presentation.xml
        const updatedPresentationXml = builder.buildObject(presentation);
        zip.file('ppt/presentation.xml', updatedPresentationXml);
        
        // 更新 presentation.xml.rels
        const updatedRelsXml = builder.buildObject(rels);
        zip.file('ppt/_rels/presentation.xml.rels', updatedRelsXml);
        
        // 删除幻灯片文件及其关联文件
        zip.remove(lastSlideFile);
        zip.remove(`ppt/slides/_rels/slide${lastSlideNumber}.xml.rels`);
        
        // 保存修改后的 PPTX
        const content = await zip.generateAsync({ type: 'nodebuffer' });
        fs.writeFileSync(pptxPath, content);
        
        console.log('# 最后一页已删除');
        
    } catch (error) {
        console.error('# 删除最后一页失败:', error.message);
        // 不抛出错误，继续执行
    }
}
// 获取页面页码链接
const getPageUrl = (page) => {
    if (page === 1) {
        return baseUrl;
    }
    const match = baseUrl.match(/^https:\/\/.*\/(.*?)\/$/);
    if (match) {
        return `${baseUrl}ppt_${match[1]}_${page}.html`;
    }
}
// 获取页面内容
const getPageContent = async (url) => {
    const response = await fetch(url, {
        headers: {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'Referer': 'https://www.1ppt.com/article/138645.html'
        }
    });
    // 获取原始二进制数据（arrayBuffer）
    const arrayBuffer = await response.arrayBuffer();
    // 转换为 Buffer（iconv-lite 需基于 Buffer 处理）
    const buffer = Buffer.from(arrayBuffer);
    // 使用 iconv-lite 转换编码
    const html = iconv.decode(buffer, 'gbk');
    return html;
}

// 延时函数
const wait = async (sec = 1) => {
    return new Promise(resolve => setTimeout(resolve, sec * 1000));
}
// 获取页面PPT ID列表
const getPptIdList = (pageContent) => {
    const dom = new jsdom.JSDOM(pageContent);
    const document = dom.window.document;
    console.log(document);
    const pptList = document.querySelectorAll('.tplist h2 a');
    console.log(pptList.length);
    const pptIdList = [];
    pptList.forEach(item => {
        pptIdList.push(item.href.replace(/\/.*?\/(\d+)\.html$/, '$1'));
    });
    return pptIdList;
}
const downloadFile = async (pptId) => {
    const browser = await puppeteer.launch({
        headless: true,
        defaultViewport: null
    });
    return new Promise(async (resolve, reject) => {
        const page = await browser.newPage();
        
        // 设置下载路径
        const downloadPath = path.resolve(__dirname, 'ppt-downloads');
        if (!fs.existsSync(downloadPath)) {
            fs.mkdirSync(downloadPath, { recursive: true });
        }
        
        // 启用下载行为
        const client = await page.target().createCDPSession();
        await client.send('Page.setDownloadBehavior', {
            behavior: 'allow',
            downloadPath: downloadPath
        });
        
        const defailUrl = `${basePath}article/${pptId}.html`;
        console.log(defailUrl);
        await page.goto(defailUrl);
        await page.click('.downurllist li a');
        const h1Tag = await page.$('h1');
        const title = (await h1Tag.evaluate(h1Tag => h1Tag.textContent, h1Tag)).replace('下载', '');
        console.log(`# 标题:${title}`);
        
        browser.on('targetcreated', async (target) => {
            if(target.type() === 'page'){
                const newPage = await target.page();
                await newPage.waitForSelector('.downloadlist .c1 a', {
                    timeout: 15000
                });
                await newPage.$eval('.downloadlist .c1 a', (el) => {
                    el.setAttribute('target', '');
                })
                
                // 创建下载监听
                const newClient = await newPage.target().createCDPSession();
                await newClient.send('Page.setDownloadBehavior', {
                    behavior: 'allow',
                    downloadPath: downloadPath
                });
                
                // 获取下载前的文件列表
                const filesBefore = fs.readdirSync(downloadPath);
                console.log('下载前文件列表:', filesBefore);
                
                // 点击下载
                await newPage.click('.downloadlist .c1 a');
                console.log('已点击下载链接，等待文件...');
                
                // 轮询检查新文件
                const checkDownload = setInterval(() => {
                    const filesNow = fs.readdirSync(downloadPath);
                    const newFiles = filesNow.filter(f => !filesBefore.includes(f));
                    
                    // 过滤掉临时文件（.crdownload 是 Chrome 下载中的临时文件）
                    const completedFiles = newFiles.filter(f => !f.endsWith('.crdownload'));
                    
                    if (completedFiles.length > 0) {
                        clearInterval(checkDownload);
                        console.log('下载完成!', completedFiles[0]);
                        const crdownloadFiles = newFiles.filter(f => f.endsWith('.crdownload'));
                        crdownloadFiles.forEach(file => {
                            try {
                                const filePath = path.join(downloadPath, file);
                                fs.unlinkSync(filePath);
                                console.log('已删除临时文件:', file);
                            } catch (err) {
                                console.warn('删除临时文件失败:', file, err.message);
                            }
                        });
                        resolve({
                            title: title,
                            downloadPath: downloadPath,
                            filename: completedFiles[0],
                            fullPath: path.join(downloadPath, completedFiles[0])
                        });
                        browser.close();
                    }
                }, 500); // 每500ms检查一次
                
                // 设置超时
                setTimeout(() => {
                    clearInterval(checkDownload);
                    reject(new Error('下载超时'));
                    browser.close();
                }, 60000); // 60秒超时
            }
        });
    })
}
const retryDownload = async (pptId, maxRetries = 3) => {
    let lastError = null;
    
    for (let attempt = 0; attempt <= maxRetries; attempt++) {
        try {
            if (attempt > 0) {
                console.log(`# 第 ${attempt} 次重试 (PPT ID: ${pptId})`);
                await wait(3); // 重试前等待3秒
            }
            
            const downloadInfo = await downloadFile(pptId);
            return downloadInfo; // 成功则返回
            
        } catch (error) {
            lastError = error;
            console.error(`# 下载失败 (尝试 ${attempt + 1}/${maxRetries + 1}): ${error.message}`);
            
            if (attempt === maxRetries) {
                // 已经是最后一次尝试
                console.error(`# 已达到最大重试次数，跳过 PPT ID: ${pptId}`);
                throw error; // 抛出错误，让调用者知道失败了
            }
        }
    }
    
    throw lastError;
}
/**
 * 从压缩包中提取 PPTX 文件
 * @param {string} archivePath - 压缩包完整路径
 * @param {string} archiveExt - 压缩包扩展名 (.zip 或 .rar)
 * @param {string} downloadPath - 下载目录路径
 * @returns {Promise<Buffer|null>} - 返回 PPTX 文件的 Buffer，失败返回 null
 */
const extractPPTXFromArchive = async (archivePath, archiveExt, downloadPath) => {
    try {
        let pptxBuffer = null;
        let pptxFilename = null;
        
        if (archiveExt === '.zip') {
            console.log('# 解压 ZIP 文件...');
            const zip = new AdmZip(archivePath);
            const zipEntries = zip.getEntries();
            
            const pptxEntry = zipEntries.find(entry => 
                !entry.isDirectory && entry.entryName.toLowerCase().endsWith('.pptx')
            );
            
            if (pptxEntry) {
                pptxBuffer = zip.readFile(pptxEntry);
                pptxFilename = pptxEntry.entryName;
                console.log(`# 找到 PPTX: ${pptxFilename}`);
            }
        } else if (archiveExt === '.rar') {
            console.log('# 解压 RAR 文件...');
            const extractor = await createExtractorFromFile({
                filepath: archivePath,
                targetPath: downloadPath
            });
            
            const extracted = extractor.extract();
            const files = [...extracted.files];
            
            const pptxFile = files.find(file => 
                file.fileHeader.name.toLowerCase().endsWith('.pptx')
            );
            
            if (pptxFile) {
                pptxBuffer = pptxFile.extraction;
                pptxFilename = pptxFile.fileHeader.name;
                console.log(`# 找到 PPTX: ${pptxFilename}`);
            }
        } else {
            console.log(`# 不支持的格式: ${archiveExt}`);
        }
        
        if (!pptxBuffer) {
            console.log(`# 警告: 压缩包中未找到 PPTX 文件`);
        }
        
        return pptxBuffer;
    } catch (error) {
        console.error('# 解压失败:', error.message);
        return null;
    }
}

/**
 * 保存 PPTX 文件到指定目录，自动处理重名
 * @param {Buffer} pptxBuffer - PPTX 文件内容
 * @param {string} title - 文件标题（用作文件名）
 * @param {string} savePath - 保存目录路径
 * @returns {string|null} - 返回最终保存的文件完整路径，失败返回 null
 */
const savePPTXFile = (pptxBuffer, title, savePath) => {
    try {
        // 清理标题中的非法文件名字符
        const cleanTitle = title.replace(/[/\\?%*:|"<>]/g, '-').trim();
        const newFilename = `${cleanTitle}.pptx`;
        const newFullPath = path.join(savePath, newFilename);
        
        // 检查文件是否已存在，避免覆盖
        let finalPath = newFullPath;
        let counter = 1;
        while (fs.existsSync(finalPath)) {
            finalPath = path.join(savePath, `${cleanTitle}_${counter}.pptx`);
            counter++;
        }
        
        // 保存 pptx 文件
        fs.writeFileSync(finalPath, pptxBuffer);
        console.log(`# PPTX 已保存: ${path.basename(finalPath)}`);
        
        return finalPath;
    } catch (error) {
        console.error('# 保存文件失败:', error.message);
        return null;
    }
}

/**
 * 处理下载的压缩包：解压、保存 PPTX、删除压缩包
 * @param {Object} downloadInfo - 下载信息对象 { title, downloadPath, filename, fullPath }
 * @returns {Promise<string|null>} - 返回最终保存的 PPTX 文件路径，失败返回 null
 */
const processDownloadedArchive = async (downloadInfo) => {
    const ext = path.extname(downloadInfo.filename).toLowerCase();
    
    // 从压缩包中提取 PPTX
    const pptxBuffer = await extractPPTXFromArchive(
        downloadInfo.fullPath, 
        ext, 
        downloadInfo.downloadPath
    );
    
    if (!pptxBuffer) {
        return null;
    }
    
    // 保存 PPTX 文件
    const finalPath = savePPTXFile(
        pptxBuffer, 
        downloadInfo.title, 
        downloadInfo.downloadPath
    );
    
    if (!finalPath) {
        return null;
    }
    
    // 删除原压缩包
    try {
        fs.unlinkSync(downloadInfo.fullPath);
        console.log(`# 已删除压缩包: ${downloadInfo.filename}`);
    } catch (error) {
        console.error('# 删除压缩包失败:', error.message);
    }
    
    return finalPath;
}

async function main() {
    
    while (page > minPage) {
        // 获取页面链接
        console.log('# 获取页面链接');
        const pageUrl = getPageUrl(page);
        console.log('# 获取当前页页面内容');
        // 获取当前页页面内容
        const pageContent = await getPageContent(pageUrl);
        console.log('# 获取PPT ID列表');
        // 获取PPT ID列表
        const pptIdList = getPptIdList(pageContent);
        
        for (const pptId of pptIdList) {
            try {
                console.log(`# 下载PPT ID:${pptId}`);
                
                // 使用重试机制下载
                const downloadInfo = await retryDownload(pptId, 3);
                console.log(`# 下载完成 ${downloadInfo.title}`);
                
                // 解压并保存 PPTX 文件
                const finalPath = await processDownloadedArchive(downloadInfo);
                
                if (!finalPath) {
                    console.log(`# 处理失败，跳过`);
                    continue;
                }
                
                // 删除 PPTX 最后一页
                await removeLastSlide(finalPath);
                
                console.log(`# 全部处理完成: ${path.basename(finalPath)}`);
                
            } catch (error) {
                // 下载失败（包括所有重试都失败），跳过这个 pptId
                console.error(`# 跳过 PPT ID ${pptId}，继续下一个...`);
                continue;
            }
            
            await wait(5);
        }
        page--;
    }
}

main();